package chapt06.fogged;
// SphereWorld.java
// OpenGL SuperBible
// Demonstrates an immersive 3D environment using actors
// and a camera.  This version adds lights and material properties
// and shadows.
// Program by Richard S. Wright Jr.

import javax.media.opengl.GL2;
import javax.media.opengl.glu.GLU;

import com.jogamp.opengl.util.gl2.GLUT;

import java.util.Random;

import shared.*;

public class Fogged
{
	static GLU glu = new GLU();
	static GLUT glut = new GLUT();
	
	static int NUM_SPHERES = 50;
	static GLFrame spheres[] = new GLFrame[NUM_SPHERES];
	static GLFrame frameCamera = new GLFrame();
	
	// Light and material data
	static float fLightPos[] = {-100.0f, 100.0f, 50.0f, 1.0f}; // Point source
	static float fNoLight[] = {0.0f, 0.0f, 0.0f, 0.0f};
	static float fLowLight[] = {0.25f, 0.25f, 0.25f, 1.0f};
	static float fBrightLight[] = {1.0f, 1.0f, 1.0f, 1.0f};
	
	static float mShadowMatrix[] = new float[16];
	
	static float yRot = 0.0f; // Rotation angle for animation
	
	//////////////////////////////////////////////////////////////////
	// This function does any needed initialization on the rendering
	// context. 
	protected static void setup(GL2 gl2, int width, int height)
	{
		int iSphere;
		Random random = new Random();
		
		// Calculate shadow matrix
		float vPoints[][] = {
			{0.0f, -0.4f, 0.0f},
			{10.0f, -0.4f, 0.0f},
			{5.0f, -0.4f, -5.0f}
		};
		
		// Grayish background
		gl2.glClearColor(fLowLight[0], fLowLight[1], fLowLight[2], fLowLight[3]);
		
		// Setup Fog parameters
		gl2.glEnable(GL2.GL_FOG); // Turn Fog on
		gl2.glFogfv(GL2.GL_FOG_COLOR, fLowLight, 0); // Set fog color to match background
		gl2.glFogf(GL2.GL_FOG_START, 5.0f);          // How far away does the fog start
		gl2.glFogf(GL2.GL_FOG_END, 30.0f);           // How far away does the fog stop
		gl2.glFogi(GL2.GL_FOG_MODE, GL2.GL_LINEAR);  // Which fog equation do I use?
		
		// Cull backs of polygons
		gl2.glCullFace(GL2.GL_BACK);
		gl2.glFrontFace(GL2.GL_CCW);
		gl2.glEnable(GL2.GL_CULL_FACE);
		gl2.glEnable(GL2.GL_DEPTH_TEST);
		
		// Setup light parameters
		gl2.glLightModelfv(GL2.GL_LIGHT_MODEL_AMBIENT, fNoLight, 0);
		gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_AMBIENT, fLowLight, 0);
		gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_DIFFUSE, fBrightLight, 0);
		gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_SPECULAR, fBrightLight, 0);
		gl2.glEnable(GL2.GL_LIGHTING);
		gl2.glEnable(GL2.GL_LIGHT0);
		
		// Get the plane equation from three points on the ground
		float vPlaneEquation[] = new float[4];
		M3D.getPlaneEquation(vPlaneEquation, vPoints[0], vPoints[1], vPoints[2]);
		
		// Calculate projection matrix to draw shadow on the ground
		M3D.makePlanarShadowMatrix(mShadowMatrix, vPlaneEquation, fLightPos);
		
		// Mostly use material tracking
		gl2.glEnable(GL2.GL_COLOR_MATERIAL);
		gl2.glColorMaterial(GL2.GL_FRONT, GL2.GL_AMBIENT_AND_DIFFUSE);
		gl2.glMateriali(GL2.GL_FRONT, GL2.GL_SHININESS, 128);
		
		// Randomly place the sphere inhabitants
		for(iSphere = 0; iSphere < NUM_SPHERES; iSphere++)
		{
			// Pick a random location between -20 and 20 at .1 increments
			float x = ((float)random.nextInt(400) - 200) * 0.1f;
			float z = ((float)random.nextInt(400) - 200) * 0.1f;
			spheres[iSphere] = new GLFrame();
			spheres[iSphere].setOrigin(x, 0.0f, z);
		}
	}

	///////////////////////////////////////////////////////////
	// Draw the ground as a series of triangle strips
	static void drawGround(GL2 gl2)
	{
		float fExtent = 20.0f;
		float fStep = 1.0f;
		float y = -0.4f;
		float iStrip, iRun;
		
		for(iStrip = -fExtent; iStrip <= fExtent; iStrip += fStep)
		{
			gl2.glBegin(GL2.GL_TRIANGLE_STRIP);
				gl2.glNormal3f(0.0f, 1.0f, 0.0f); // All Point up
				
				for(iRun = fExtent; iRun >= -fExtent; iRun -= fStep)
				{
					gl2.glVertex3f(iStrip, y, iRun);
					gl2.glVertex3f(iStrip + fStep, y, iRun);
				}
			gl2.glEnd();
		}
	}
	
	///////////////////////////////////////////////////////////////////////
	// Draw random inhabitants and the rotating torus/sphere duo
	static void drawInhabitants(GL2 gl2, boolean nShadow)
	{
		int i;
		
		if(! nShadow)
			yRot += 0.5f;
		else
			gl2.glColor3f(0.0f, 0.0f, 0.0f);
		
		// Draw the randomly located spheres
		if(! nShadow)
			gl2.glColor3f(0.0f, 1.0f, 0.0f);
		
		for(i = 0; i < NUM_SPHERES; i++)
		{
			gl2.glPushMatrix();
			spheres[i].applyActorTransform(gl2);
			glut.glutSolidSphere(0.3f, 17, 9);
			gl2.glPopMatrix();
		}
		
		gl2.glPushMatrix();
			gl2.glTranslatef(0.0f, 0.1f, -2.5f);
			
			if(! nShadow)
				gl2.glColor3f(0.0f, 0.0f, 1.0f);
			
			gl2.glPushMatrix();
				gl2.glRotatef(-yRot * 2.0f, 0.0f, 1.0f, 0.0f);
				gl2.glTranslatef(1.0f, 0.0f, 0.0f);
				glut.glutSolidSphere(0.1f, 17, 9);
			gl2.glPopMatrix();
			
			if(! nShadow)
			{
				// Torus alone will be specular
				gl2.glColor3f(1.0f, 0.0f, 0.0f);
				gl2.glMaterialfv(GL2.GL_FRONT, GL2.GL_SPECULAR, fBrightLight, 0);
			}
			
			gl2.glRotatef(yRot, 0.0f, 1.0f, 0.0f);
			GLT.drawTorus(gl2, 0.35f, 0.15f, 61, 37);
			gl2.glMaterialfv(GL2.GL_FRONT, GL2.GL_SPECULAR, fNoLight, 0);
		gl2.glPopMatrix();
	}
	
	// Called to draw scene
	protected static void render(GL2 gl2, int w, int h)
	{
		float fAspect;
		
		// Prevent a divide by zero, when window is too short
		// (you can't make a window of zero width).
		if(h == 0)
			h = 1;
		
		gl2.glViewport(0, 0, w, h);
		
		fAspect = (float)w / (float)h;
		
		// Reset the coordinate system before modifying
		gl2.glMatrixMode(GL2.GL_PROJECTION);
		gl2.glLoadIdentity();
		
		// Set the clipping volume
		glu.gluPerspective(35.0f, fAspect, 1.0f, 50.0f);
		
		gl2.glMatrixMode(GL2.GL_MODELVIEW);
		gl2.glLoadIdentity();
		
		// Clear the window with current clearing color
		gl2.glClear(GL2.GL_COLOR_BUFFER_BIT | GL2.GL_DEPTH_BUFFER_BIT);
		
		gl2.glPushMatrix();
			frameCamera.applyCameraTransform(gl2);
			
			// Position light before any other transformations
			gl2.glLightfv(GL2.GL_LIGHT0, GL2.GL_POSITION, fLightPos, 0);
			
			// Draw the ground
			gl2.glColor3f(0.6f, 0.4f, 0.1f);
			drawGround(gl2);
			
			// Draw shadows first
			gl2.glDisable(GL2.GL_DEPTH_TEST);
			gl2.glDisable(GL2.GL_LIGHTING);
			gl2.glPushMatrix();
				gl2.glMultMatrixf(mShadowMatrix, 0);
				drawInhabitants(gl2, true);
			gl2.glPopMatrix();
			gl2.glEnable(GL2.GL_LIGHTING);
			gl2.glEnable(GL2.GL_DEPTH_TEST);
			
			// Draw inhabitants normally
			drawInhabitants(gl2, false);

		gl2.glPopMatrix();
			
		// Do the buffer Swap
		gl2.glFlush();
	}
}